#include <errno.h>
#include <unistd.h>
#include <execinfo.h>
#include <semaphore.h>
#include <signal.h>

#include <iostream>
#include <memory>
#include <string>
#include "utilspp.hpp"
#include "logger.hpp"
#include "file_util.h"
#include "common_util.h"
#include "networkUtils.h"
#include "mk_mediakit.h"
#include "HttpServer.hpp"
#include "SysParams.hpp"
#include "StreamProxyer.hpp"

sem_t sem_signal;
static std::string  gl_pro_name;
static int          gl_pro_exit = 0;
static constexpr int MAX_STACK_FRAMES = 64;

static void signal_log_error(int sig) 
{
    LOGE("Program terminated with signal {}", sig);
    void *addrlist[MAX_STACK_FRAMES + 1];

    // 获取堆栈地址
    int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void *));
    if (addrlen == 0) {
        LOGE("Stack is empty!");
        return;
    }

    // 解析堆栈地址为可读字符串
    char **symbollist = backtrace_symbols(addrlist, addrlen);

    for (int i = 1; i < addrlen; i++) {
        LOGE(symbollist[i]);
    }

    free(symbollist);
}

static void SigHandler(int sig) {
    std::cout<<"Program get signal "<<sig<<std::endl;
    if(sig == SIGINT || sig == SIGTERM){
        std::cout<<"got SIGINT or SIGTERM so that exit"<<std::endl;
        gl_pro_exit = 1;
        return ;
    }else if(sig == SIGPIPE){
        LOGI("get SIGPIPE");
        return ;
    }
    signal_log_error(sig);
    sem_post(&sem_signal);
    exit(0);
}

static void InitSignals() {
    sem_init(&sem_signal, 0, 0);

    /*signal handlers*/
    signal(SIGINT, SigHandler);
    signal(SIGTERM, SigHandler);
    signal(SIGPIPE, SigHandler);
    signal(SIGSEGV, SigHandler);
}

static bool get_pro_name(char *pro_name, int srclen, int argc, char *argv[])
{
    if(argc < 1 || argv[0] == nullptr)return false;
    if(pro_name == nullptr || srclen <= 0) return false;
    int dstlen = strlen(argv[0]);
    if(dstlen >= srclen) return false;
    memset(pro_name, 0 , srclen);
    char *pro_name_ = (char *)malloc(strlen(argv[0]) + 1);
    if (pro_name_ == nullptr) {
        printf("Couldn't allocate memory for pro_name_");
        return false;
    }
    char *filename = nullptr;
    memset(pro_name_, 0, strlen(argv[0]) + 1);
    strcpy(pro_name_, argv[0]);
    filename = strrchr(pro_name_, '/');
    if (filename != nullptr) {
        strcpy(pro_name, filename + 1);
    }else{
        strcpy(pro_name, pro_name_);
    }
    free(pro_name_);
    pro_name_ = nullptr;
    return true;
}

static void init_ZLMediaKit()
{
    mk_config config;
    config.ini = NULL;
    config.ini_is_path = 0;
    config.log_level = 0;
    config.log_mask = LOG_CONSOLE;
    config.ssl = NULL;
    config.ssl_is_path = 1;
    config.ssl_pwd = NULL;
    config.thread_num = 0;
    mk_env_init(&config);
    int port = find_free_port(12080, 100);
    if(port <= 0){
        std::runtime_error("not found available port");
    }
    LOGI("server port: {}", port);
    StreamProxyer::m_server_port = port;
    char local_ip[16] = {0};
    if(get_local_ip(local_ip, sizeof(local_ip)) == 0){
        StreamProxyer::m_local_ip = local_ip;
    }else{
        StreamProxyer::m_local_ip = "127.0.0.1";
    }
    LOGI("local ip: {}", StreamProxyer::m_local_ip);
    
    if(mk_http_server_start(StreamProxyer::m_server_port, 0) != 0){
        std::runtime_error("mk_http_server_start failed");
    }
}

void *monitor_self_thread(void *arg)
{
    (void)arg;
    long virtual_memory = 0;
    long physical_memory = 0;
    double cpu = 0;
    while(!gl_pro_exit){
        sleep(5);
        get_usage(&virtual_memory, &physical_memory, &cpu);
        std::string usage_ = UtilsPP::formatString("VIRT: %ldMB RES: %ldKB CPU: %.2f%%", 
                                virtual_memory, physical_memory,cpu);
        LOGI(usage_);
    }
    return NULL;
}

int main(int argc, char *argv[]) 
{
    pthread_t monitid = 0;
    char pro_name[128] = {0};
    if (!get_pro_name(pro_name, sizeof(pro_name), argc, argv)) {
        printf("Couldn't allocate memory for pro_name_");
        return -1;
    }
    gl_pro_name = std::string(pro_name);
    size_t max_size_bytes = (5 << 20);  // 单个 log 文件最大大小，默认 1MB
    size_t max_files = 5;
    InitSignals();
    
    std::string rootDir = "";
    bool m_log_enabled = true;
    std::string m_log_dir = "logs";
    spdlog::set_error_handler([](const std::string &msg) {
        std::cerr << "---------- Spdlog error: " << msg << std::endl;
    });
    FileUtil::create_dir(m_log_dir);
    Logger::init(m_log_dir, pro_name, m_log_enabled, max_size_bytes, max_files);
    LOGI("pro_name: {} log_enable: {} log_dir: {}", pro_name, m_log_enabled, m_log_dir);
    init_ZLMediaKit();
    auto server_params = SysParams::getInstance();
    if (server_params == nullptr) {
        LOGE("Couldn't get server_params");
        return -1;
    }
    server_params->load();
    server_params->save();
    int ret = pthread_create(&monitid, NULL, monitor_self_thread, NULL);
    if(ret != 0) {
        LOGE("pthread_create monitor_self_thread failed");
        return -1;
    }
    HttpServer *http_server = nullptr;
    try{
        http_server = new HttpServer(28080);
        http_server->Start();
    }catch(std::exception &e){
        LOGE("Exception: {}", e.what());
    }
    pthread_join(monitid, NULL);
    if(http_server){
        http_server->Stop();
        if(http_server->joinable()){
            http_server->join();
        }
        delete http_server;
        http_server = nullptr;
    }
    mk_stop_all_server();
    return 0;
}